﻿using Microsoft.Extensions.Logging;
using System.Net.NetworkInformation;

namespace Away.NetMap.Nmap;

public class IPScannerParams
{
    /// <summary>
    /// 开始IP
    /// </summary>
    public string Start { get; set; } = null!;
    /// <summary>
    /// 结束IP
    /// </summary>
    public string End { get; set; } = null!;
    /// <summary>
    /// 线程数
    /// </summary>
    public int Threads { get; set; } = 100;
    /// <summary>
    /// 超时时间
    /// </summary>
    public int Timeout { get; set; } = 300;
    /// <summary>
    /// 是否扫描私有地址 0:否
    /// </summary>
    public int IsScanPrivateIP { get; set; }
}


[ServiceInject(ServiceLifetime.Scoped, true)]
public class IPScannerService
{
    private readonly IServiceScope _scope;
    public IPScannerService(IServiceProvider serviceProvider)
    {
        _scope = serviceProvider.CreateScope();
        PingCompleted += Completed;
    }

    private IServiceProvider ServiceProvider => _scope.ServiceProvider;
    private IPRepository Rep => ServiceProvider.GetRequiredService<IPRepository>();
    private ILogger<IPScannerService> Logger => ServiceProvider.GetRequiredService<ILogger<IPScannerService>>();

    private IPScannerParams? _params;
    public void Run(IPScannerParams p, CancellationToken token)
    {
        System.Diagnostics.Stopwatch stopwatch = new();
        stopwatch.Start();

        _params = p;
        var start = IPHelper.ToInt(p.Start);
        var end = IPHelper.ToInt(p.End);

        var total = Math.Abs(end - start) + 1;
        Logger.LogInformation("扫描IP区间：[{},{}] 共{}个", p.Start, p.End, total);
        var threads = p.Threads;
        var ip = start;
        while (true)
        {
            if (token.IsCancellationRequested)
            {
                break;
            }
            var num = threads;
            if (ip > end)
            {
                break;
            }
            var caps = end - ip;
            if (caps > 0 && caps < threads)
            {
                num = caps + 1;
            }

            Enumerable.Range(ip, num)
            .Select(IPHelper.ToString)
            .AsParallel()
            .OrderByDescending(o => o)
            .WithDegreeOfParallelism(threads)
            .WithCancellation(token)
            .ForAll(async o => await ScanOne(o));

            ip += threads;
        }
        Logger.LogInformation("扫描IP区间：[{},{}] 共{}个 耗时：{}", p.Start, p.End, total, stopwatch.Elapsed);
    }


    private async Task ScanOne(string ip)
    {
        // 跳过私有地址
        if (_params!.IsScanPrivateIP != 0 && IPBlackList.IsPrivateIP(IPHelper.ToInt(ip)))
        {
            return;
        }

        using var icmp = new Ping();
        var reply = await icmp.SendPingAsync(ip, _params!.Timeout);
        PingCompleted?.Invoke(new PingCompletedEventArgs { IP = ip, Success = reply?.Status == IPStatus.Success });
    }

    private delegate void PingCompletedEventHandler(PingCompletedEventArgs args);
    private event PingCompletedEventHandler PingCompleted;
    private static readonly object _lock = new();
    private void Completed(PingCompletedEventArgs args)
    {
        lock (_lock)
        {
            if (args.Success)
            {
                Logger.LogTrace("{}", args.IP);
                Rep.AddIp(args.IP);
            }
        }
    }
}

public class PingCompletedEventArgs : EventArgs
{
    public string IP { get; set; } = null!;
    public bool Success { get; set; }
}
